<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>手写Promise</title>
</head>
<body>

  <script>
    const PENDING = 'pending' // 初始/未确定
    const RESOLVED = 'resolved' // 成功
    const REJECTED = 'rejected' // 失败

    function Promise(excutor) {
      const self = this
      self.status = PENDING
      self.data = undefined
      /* 
      {
        onResolved: function (){},
        onRejected: function (){}
      }
      */
      self.callbacks = []  // 用来保存所有待处理的成功和失败的回调函数

      function resolve(value) {
        // 如果当前promise不是pending, 直接结束
        if (self.status!==PENDING) return

        // 立即改状态
        self.status = RESOLVED
        // 立即存数据
        self.data = value
        // 异步调用待处理的成功的回调函数
        setTimeout(() => {
          self.callbacks.forEach(callbackObj => {
            callbackObj.onResolved(value)
          })
        })
      }

      function reject(reason) {
        // 如果当前promise不是pending, 直接结束
        if (self.status!==PENDING) return

        // 立即改状态
        self.status = REJECTED
        // 立即存数据
        self.data = reason
        // 异步调用待处理的成功的回调函数
        setTimeout(() => {
          self.callbacks.forEach(callbackObj => {
            callbackObj.onRejected(reason)
          })
        })
      }

      try {
        // 立即执行执行器函数
        excutor(resolve, reject)
      } catch (error) {
        reject(error)
      }
    }
    
    /* 
    1. 返回一个新的promise
    2. 新的promise的状态由onResolved/onRejected执行的结果决定
      1). 返回一个非promise   ===> 成功
      2). 抛出异常  ===> 失败
      3). 返回一个promise  ===> 与这个promise的结果一致
    3. 在then中要对onResolved/onRejected进行处理: 根据当前promise的状态来操作
      1). resolved: 异步调用onResolved
      2). rejected: 异步调用onRejected
      3). pending: 将onResolved/onRejected保存到callbacks中
    */
    Promise.prototype.then = function (onResolved, onRejected) {
      const self = this

      // 如果onResolved不是函数, 需要针value传递下去
      onResolved = typeof onResolved === 'function' ? onResolved : value => value
      // 如果onRejected不是函数, 需要针reason传递下去
      onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}

      // 返回一个新的promise
      return new Promise((resolve, reject) => {

        /* 
        调用指定的回调函数
        根据执行的结果, 更新返回promise的状态
        */
        function handleCallback(callback) {
          try {
            const result = callback(self.data)
            if (result instanceof Promise) {
              result.then(
                value => {resolve(value)},
                reason => {reject(reason)}
              )
              // result.then(resolve, reject)
            } else {
              resolve(result)
            }
          } catch (error) {
            reject(error)
          } 
        }

        if (self.status===RESOLVED) {
          setTimeout(() => {
            handleCallback(onResolved)
          })
          
        } else if (self.status===REJECTED) {
          setTimeout(() => {
            handleCallback(onRejected)
          })
        } else { // PENDING
          // 保存回调函数
          self.callbacks.push({
            onResolved: value => handleCallback(onResolved),
            onRejected: reason => handleCallback(onRejected)
          })
        }
      })
    }

    Promise.prototype.catch = function (onRejected) {
      return this.then(undefined, onRejected)
    }

    Promise.resolve = function (value) { // value有可能是一个promise
      return new Promise((resolve, reject) => {
        if (value instanceof Promise) {
          value.then(resolve, reject)
        } else {
          resolve(value)
        }
      })
    }

    Promise.reject = function (reason) {
      return new Promise((resolve, reject) => {
        reject(reason)
      })
    }

    Promise.resolveDelay = function (value, time) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (value instanceof Promise) {
            value.then(resolve, reject)
          } else {
            resolve(value)
          }
        }, time)
      })
    }

    Promise.rejectDelay = function (reason, time) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          reject(reason)
        }, time)
      })
    }

    Promise.race = function (promises) {
      return new Promise((resolve, reject) => {
        promises.forEach(p => p.then(resolve, reject))
      })
    }

    Promise.all = function (promises) {
      return new Promise((resolve, reject) => {

        const values = []
        let resolvedCount = 0

        promises.forEach((p, index) => {
          p.then(
            value => {
              resolvedCount++
              values[index] = value
              if (resolvedCount===promises.length) {
                resolve(values)
              }
            },
            reason => reject(reason)
          )
        })
      })
    }



  </script>

  <script>
    const p1 = Promise.resolve(5)
    const p2 = Promise.resolve(Promise.reject(6))
    const p3 = Promise.resolveDelay(7, 1000)
    const p4 = Promise.rejectDelay(8, 2000)
    // p1.then(value => console.log('p1', value))
    // p2.catch(reason => console.log('p2', reason))
    // p3.then(value => console.log('p3', value))
    // p4.catch(reason => console.log('p4', reason))

    // const p5 = Promise.race([p3, p1])
    // const p5 = Promise.race([p3, p2])
    // p5.then(
    //   value => console.log('race onResolved()', value),
    //   reason => console.log('race onRejected()', reason),
    // )

    // const p6 = Promise.all([p1, p3])
    const p6 = Promise.all([p2, p3])
    p6.then(
      vales => console.log('all onResolved()', vales),
      reason => console.log('all onRejected()', reason),
    )
  </script>


<!-- 
  <script>
    new Promise((resolve, reject) => {  // excutor
      // 遍历异步任务
      setTimeout(() => {
        // 如果成功
        // resolve(1)
        // 如果失败
        reject(2)
      }, 1000)
      // throw 3
    })
    .then(
      value => {
        console.log('onResolved1()', value)
      }, // onResolved
      reason => {
        console.log('onRejected1()', reason)
        throw 3
      }  // onRejected
    )
    .then(
      value => {
        console.log('onResolved2()', value)
      }, // onResolved
      reason => {
        console.log('onRejected2()', reason)
      }  // onRejected
    )
    .then(
      value => {
        console.log('onResolved3()', value)
      }
    )
    .catch(
      reason => {
        console.log('catch()', reason)
      }
    )
    .then(
      value => {
        console.log('onResolved4()', value)
      },
      reason => {
        console.log('onRejected4()', reason)
      }  
    )
    console.log('new Promise()之后')

  </script> -->
</body>
</html>